<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>作为属性名的Symbol</title>
</head>
<body>
  <script>
    // 由于每一个Symbol值都是不相等的，这意味着Symbol值可以作为标识符，用户对象的属性名。
    // 就能保住不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用，能防止某一个键被不小心改写或覆盖
    var mySymbol = Symbol();
    // 第一种写法
    var a = {};
    a[mySymbol] = 'Hello!';
    // 第二种写法
    var a = {
      [mySymbol]: 'Hello!'
    }
    // 第三种写法
    var a = {};
    Object.defineProperty(a, mySymbol, {
      configurable: true,
      enumerable: true,
      value: 'Hello!'
    })
    // 以上写法都得到同样的结果
    console.log(a[mySymbol]) // Hello!
    // 注意：Symbol值作为对象属性名时，不能用点运算符。
    var mySymbol = Symbol();
    var a = {};
    a.mySymbol = 'Hello';
   console.log(a[mySymbol]); // undefined
   console.log(a['mySymbol'])// Hello
   /**
    * 上面代码中，因为点运算符后面总是字符串，所以不会读取mySymbol作为标识名所指代的那个值
    * 导致a的属性名实际上是一个字符串，而不是一个Symbol值。
    *
    * 同理，在对象内部，使用Symbol值定义属性时，Symbol值必须放在方括号之中
   */
  var s = Symbol();
  var obj = {
    [s]:function(arg) {
      //...
      console.log(arg)//123
    }
  }
  obj[s](123);
  // 采用增强对象的写法，上面的代码obj对象可以写的更简洁一些
  // var obj = {
  //   [s](arg) {
  //     //...
  //     console.log(arg)//123
  //   }
  // }
  // Symbol类型还可以用于定义一组常量，保证这组常量的值都是不相等的。
  var log = {};
  log.levels = {
    DEBUG: Symbol('debug'),
    INFO: Symbol('info'),
    WARN: Symbol('warn')
  }
  console.log(log.levels.DEBUG, 'debug message'); // Symbol(debug) "debug message"
  console.log(log.levels.INFO, 'info message'); // Symbol(info) "info message"
  // 还需注意的一点是，Symbol值作为属性名时，该属性还是公开属性，不是私有属性。
  </script>
</body>
</html>